using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace IppServer.Host.Discovery
{
    /// <summary>
    /// 私有 UDP 发现服务（与 DiscoveryTests/DiscoveryServer 使用相同协议）
    /// 始终监听固定端口，供局域网客户端发现 IPP 打印机 URI。
    /// </summary>
    internal sealed class PrivateDiscoveryServer : IDisposable
    {
        private const int ListenPort = 36669;
        private const string RequestMagic = "IPP_DISCOVERY_REQUEST";

        private readonly Action<string> _log;

        private UdpClient _udpClient;
        private CancellationTokenSource _cts;
        private Task _listenTask = Task.CompletedTask;
        private readonly object _syncRoot = new object();

        // 当前共享队列配置快照
        private string _basePrefix;
        private Dictionary<string, string> _queueToPrinter = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
        // TCP/IP RAW 打印机任务列表
        private List<Configuration.TcpPrinterTask> _tcpTasks = new List<Configuration.TcpPrinterTask>();

        public PrivateDiscoveryServer(Action<string> logCallback)
        {
            _log = logCallback ?? (_ => { });
        }

        /// <summary>
        /// 更新当前配置（在主程序启动或热更新时调用）
        /// </summary>
        public void UpdateConfig(string basePrefix, System.Collections.Generic.IReadOnlyDictionary<string, string> queueToPrinter, System.Collections.Generic.IReadOnlyList<Configuration.TcpPrinterTask> tcpTasks = null)
        {
            lock (_syncRoot)
            {
                _basePrefix = basePrefix;
                if (queueToPrinter != null)
                {
                    var dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                    foreach (var kvp in queueToPrinter)
                    {
                        dict[kvp.Key] = kvp.Value;
                    }
                    _queueToPrinter = dict;
                }
                else
                {
                    _queueToPrinter = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                }
                
                // 更新 TCP 任务列表
                if (tcpTasks != null)
                {
                    _tcpTasks = new List<Configuration.TcpPrinterTask>(tcpTasks);
                }
                else
                {
                    _tcpTasks = new List<Configuration.TcpPrinterTask>();
                }
            }
        }

        /// <summary>
        /// 启动 UDP 发现服务（如果已经启动则忽略）
        /// </summary>
        public void Start()
        {
            lock (_syncRoot)
            {
                if (_udpClient != null)
                {
                    return;
                }

                try
                {
                    TryEnsureFirewallRule();
                }
                catch (Exception ex)
                {
                    _log($"[Discovery] 添加防火墙规则失败：{ex.Message}");
                }

                try
                {
                    _cts = new CancellationTokenSource();
                    _udpClient = new UdpClient(ListenPort)
                    {
                        EnableBroadcast = true
                    };
                    _listenTask = Task.Run(() => ListenLoopAsync(_cts.Token));

                    _log($"[Discovery] 私有 UDP 发现服务已启动，端口 {ListenPort}");
                }
                catch (Exception ex)
                {
                    _log($"[Discovery] 启动 UDP 发现服务失败：{ex.Message}");
                    _udpClient = null;
                    try
                    {
                        _cts?.Cancel();
                    }
                    catch
                    {
                    }
                }
            }
        }

        /// <summary>
        /// 停止 UDP 发现服务
        /// </summary>
        public void Stop()
        {
            lock (_syncRoot)
            {
                try
                {
                    _cts?.Cancel();
                }
                catch
                {
                }

                try
                {
                    _udpClient?.Close();
                }
                catch
                {
                }

                _udpClient = null;
                _cts = null;
            }

            try
            {
                _listenTask.Wait(TimeSpan.FromSeconds(2));
            }
            catch
            {
                // 忽略结束时异常
            }
        }

        private async Task ListenLoopAsync(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                UdpClient client;
                lock (_syncRoot)
                {
                    client = _udpClient;
                }

                if (client == null)
                {
                    break;
                }

                try
                {
                    var result = await client.ReceiveAsync().ConfigureAwait(false);
                    HandleRequest(result.RemoteEndPoint, result.Buffer);
                }
                catch (ObjectDisposedException)
                {
                    break;
                }
                catch (SocketException)
                {
                    // 端口被关闭或网络错误时退出循环
                    break;
                }
                catch (Exception ex)
                {
                    _log($"[Discovery] 处理 UDP 请求时出错：{ex.Message}");
                }
            }
        }

        private void HandleRequest(IPEndPoint remoteEndPoint, byte[] data)
        {
            var text = Encoding.UTF8.GetString(data ?? new byte[0]);
            _log($"[Discovery] 收到来自 {remoteEndPoint} 的数据: {text}");

            if (!string.Equals(text, RequestMagic, StringComparison.Ordinal))
            {
                return;
            }

            string basePrefix;
            Dictionary<string, string> queues;
            List<Configuration.TcpPrinterTask> tcpTasks;
            lock (_syncRoot)
            {
                basePrefix = _basePrefix;
                queues = new Dictionary<string, string>(_queueToPrinter, StringComparer.OrdinalIgnoreCase);
                tcpTasks = new List<Configuration.TcpPrinterTask>(_tcpTasks);
            }

            var localIp = GetLocalIpAddress(remoteEndPoint.Address) ?? "127.0.0.1";
            var port = ExtractPortFromPrefix(basePrefix);
            var path = ExtractPathFromPrefix(basePrefix);

            string printersPart = BuildPrintersPart(queues, localIp, port, path);
            string tcpPrintersPart = BuildTcpPrintersPart(tcpTasks, localIp);

            var responseBuilder = new StringBuilder();
            responseBuilder.Append("IPP_SERVER|");
            responseBuilder.Append("Name=").Append(Environment.MachineName);
            responseBuilder.Append(";IP=").Append(localIp);
            responseBuilder.Append(";Port=").Append(port);
            responseBuilder.Append(";Printers=").Append(printersPart ?? string.Empty);
            if (!string.IsNullOrEmpty(tcpPrintersPart))
            {
                responseBuilder.Append(";TcpPrinters=").Append(tcpPrintersPart);
            }

            var response = responseBuilder.ToString();
            var respBytes = Encoding.UTF8.GetBytes(response);

            UdpClient client;
            lock (_syncRoot)
            {
                client = _udpClient;
            }

            if (client == null)
            {
                return;
            }

            client.Send(respBytes, respBytes.Length, remoteEndPoint);
            _log($"[Discovery] 已回复 {remoteEndPoint}: {response}");
        }

        private static string BuildPrintersPart(Dictionary<string, string> queues, string localIp, int port, string path)
        {
            if (queues == null || queues.Count == 0)
            {
                return string.Empty;
            }

            if (string.IsNullOrEmpty(path))
            {
                path = "/printers/";
            }
            if (!path.StartsWith("/"))
            {
                path = "/" + path;
            }
            if (!path.EndsWith("/"))
            {
                path += "/";
            }

            var sb = new StringBuilder();
            var first = true;
            foreach (var kvp in queues)
            {
                var queueName = kvp.Key ?? string.Empty;
                var printerName = kvp.Value ?? string.Empty;

                // 美化显示名称：物理打印机名称 + 队列名
                var displayName = string.IsNullOrWhiteSpace(printerName)
                    ? queueName
                    : $"{printerName} ({queueName})";

                var trimmedQueue = queueName.Trim('/').Trim();
                var uri = string.IsNullOrEmpty(trimmedQueue)
                    ? $"http://{localIp}:{port}{path.TrimEnd('/')}"
                    : $"http://{localIp}:{port}{path}{trimmedQueue}";

                if (!first)
                {
                    sb.Append("|");
                }
                first = false;

                sb.Append(displayName);
                sb.Append(",");
                sb.Append(uri);
            }

            return sb.ToString();
        }

        /// <summary>
        /// 构建 TCP/IP RAW 打印机信息部分
        /// 格式：displayName,ip:port|displayName,ip:port|...
        /// </summary>
        private static string BuildTcpPrintersPart(List<Configuration.TcpPrinterTask> tcpTasks, string localIp)
        {
            if (tcpTasks == null || tcpTasks.Count == 0)
            {
                return string.Empty;
            }

            var sb = new StringBuilder();
            var first = true;
            foreach (var task in tcpTasks)
            {
                if (string.IsNullOrWhiteSpace(task.PrinterName))
                {
                    continue;
                }

                var printerName = task.PrinterName.Trim();
                var tcpPort = task.Port <= 0 ? 9100 : task.Port;
                
                // 格式：打印机名称,IP:端口
                if (!first)
                {
                    sb.Append("|");
                }
                first = false;

                sb.Append(printerName);
                sb.Append(",");
                sb.Append(localIp);
                sb.Append(":");
                sb.Append(tcpPort);
            }

            return sb.ToString();
        }

        /// <summary>
        /// 选择一个适合返回给远端客户端的本地 IPv4 地址：
        /// 1. 优先选择与远端在同一 /24 网段的地址；
        /// 2. 其次选择常见内网地址 10.x / 172.16-31.x / 192.168.x.x；
        /// 3. 否则返回第一个 IPv4 地址。
        /// </summary>
        private static string GetLocalIpAddress(IPAddress remoteIp)
        {
            try
            {
                var hostName = Dns.GetHostName();
                var addrs = Dns.GetHostAddresses(hostName);
                var ipv4 = new List<IPAddress>();
                foreach (var addr in addrs)
                {
                    if (addr.AddressFamily == AddressFamily.InterNetwork)
                    {
                        ipv4.Add(addr);
                    }
                }

                if (ipv4.Count == 0)
                {
                    return null;
                }

                if (remoteIp != null && remoteIp.AddressFamily == AddressFamily.InterNetwork)
                {
                    var remoteBytes = remoteIp.GetAddressBytes();
                    foreach (var addr in ipv4)
                    {
                        var localBytes = addr.GetAddressBytes();
                        if (localBytes.Length == 4 &&
                            localBytes[0] == remoteBytes[0] &&
                            localBytes[1] == remoteBytes[1] &&
                            localBytes[2] == remoteBytes[2])
                        {
                            return addr.ToString();
                        }
                    }
                }

                foreach (var addr in ipv4)
                {
                    var b = addr.GetAddressBytes();
                    if (b[0] == 10 ||
                        (b[0] == 172 && b[1] >= 16 && b[1] <= 31) ||
                        (b[0] == 192 && b[1] == 168))
                    {
                        return addr.ToString();
                    }
                }

                return ipv4[0].ToString();
            }
            catch
            {
                return null;
            }
        }

        private static int ExtractPortFromPrefix(string prefix)
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                return 8631;
            }

            try
            {
                var match = System.Text.RegularExpressions.Regex.Match(prefix, @":(\d+)/");
                if (match.Success && int.TryParse(match.Groups[1].Value, out var parsedPort) && parsedPort > 0)
                {
                    return parsedPort;
                }

                var uri = new Uri(prefix);
                if (uri.Port > 0)
                {
                    return uri.Port;
                }

                match = System.Text.RegularExpressions.Regex.Match(prefix, @":(\d+)");
                if (match.Success && int.TryParse(match.Groups[1].Value, out parsedPort) && parsedPort > 0)
                {
                    return parsedPort;
                }
            }
            catch
            {
            }

            return 8631;
        }

        private static string ExtractPathFromPrefix(string prefix)
        {
            if (string.IsNullOrWhiteSpace(prefix))
            {
                return "/printers/";
            }

            try
            {
                var uri = new Uri(prefix);
                var path = uri.AbsolutePath;
                if (string.IsNullOrEmpty(path) || path == "/")
                {
                    return "/printers/";
                }

                if (!path.EndsWith("/"))
                {
                    path += "/";
                }

                return path;
            }
            catch
            {
                return "/printers/";
            }
        }

        /// <summary>
        /// 为 UDP 发现端口尝试添加防火墙入站规则（使用 netsh，兼容 Win7/10/11）
        /// </summary>
        private static void TryEnsureFirewallRule()
        {
            try
            {
                var ruleName = "IPP Discovery UDP " + ListenPort;
                var psi = new ProcessStartInfo
                {
                    FileName = "netsh",
                    Arguments = "advfirewall firewall add rule name=\"" + ruleName +
                                "\" dir=in action=allow protocol=UDP localport=" + ListenPort,
                    CreateNoWindow = true,
                    UseShellExecute = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true
                };

                using (var proc = Process.Start(psi))
                {
                    if (proc == null)
                    {
                        return;
                    }

                    proc.WaitForExit(5000);
                }
            }
            catch
            {
                // 忽略防火墙配置错误，不影响主程序运行
            }
        }

        public void Dispose()
        {
            Stop();
        }
    }
}


